/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.http;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.Provider;
import java.security.Security;

import javax.net.ssl.SSLServerSocketFactory;

/**
 * Experiment with using HTTP over sockets $Date$ $Revision$
 */

public class HTTPSocket {
	int transaction = 0;
	int contentLength = 0;
	int port = 12345;
	String host;
	String proxyHost = "ryhn.ucs.indiana.edu";
	int proxyPort = 80;
	String headers = "POST /" + transaction + "HTTP/1.1\n"
			+ "Proxy-Connection: Keep-Alive\n" + "Progma: no-cache\n"
			+ "Content-Type: application/octet-stream\n" + "Content-Length: ";
	ServerSocket serverSocket;
	Socket socket;

	public static void main(String[] args) {
		usage(args);
		try {
			new HTTPSocket().execute(args);
			// new HTTPSocket().dialog();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void execute(String[] args) throws Exception {

		if (args[0].equals("client")) {
			host = args[1];
			port = Integer.parseInt(args[2]);
			InetAddress proxyIa = InetAddress.getByName(proxyHost);
			socket = new Socket(proxyIa, proxyPort);
			OutputStream os = socket.getOutputStream();
			InputStream is = socket.getInputStream();
			if (doConnect(os, is)) {
				System.out.println("begin dialogue");
				dialogue(os, is, true);
			}
		}
		else if (args[0].equals("server")) {
			System.out.println("HTTP:- listening on port " + port);
			Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
			Provider[] p = Security.getProviders();
			System.out.println("providers list " + p);
			serverSocket = SSLServerSocketFactory.getDefault()
					.createServerSocket(port);
			socket = serverSocket.accept();
			System.out.println("connection from "
					+ socket.getInetAddress().getHostName());
			OutputStream os = socket.getOutputStream();
			InputStream is = socket.getInputStream();
			dialogue(os, is, false);
		}
		if (socket != null) socket.close();
		System.exit(1);
	}

	void dialogue(OutputStream out, InputStream in, boolean isClient)
			throws Exception {
		String remote, local = InetAddress.getLocalHost().getHostName();

		if (isClient) {
			remote = host;
			InboundThread ibt = new InboundThread(in, remote);
			ibt.start();
		}
		else {
			remote = socket.getInetAddress().getHostName();
			InboundThread ibt = new InboundThread(in, remote);
			ibt.start();
		}
		BufferedReader reader = new BufferedReader(new InputStreamReader(
				System.in));
		String line = "";
		while ((line = reader.readLine()) != null) {
			line += "\n";
			out.write(line.getBytes());
			out.flush();
		}
		reader.close();
		System.out.println("exit dialogue method");
	}

	static void usage(String[] args) {
		if (args.length != 1 && args.length != 3) {
			System.out
					.println("Usage:\n\t'java HTTPSocket' ['client' <serverhost> <port> | 'server']");
			System.exit(1);
		}
	}

	boolean doConnect(OutputStream out, InputStream in) throws IOException {
		String cmd = "CONNECT " + host + ":" + port
				+ " HTTP/1.0\r\nUser-Agent: narada\r\n\r\n";
		out.write(cmd.getBytes("ASCII7"));
		out.flush();
		/*
		 * int ch; ByteArrayOutputStream baos = new ByteArrayOutputStream();
		 * while ((ch = in.read()) > 0) { baos.write(ch); } String replyStr =
		 * new String(baos.toByteArray());
		 */
		String replyStr = readHttpReply(in);
		System.out.println(replyStr);
		String lowerCase = replyStr.toLowerCase();
		if (lowerCase.indexOf("200") > 0 && lowerCase.indexOf("connection") > 0
				&& lowerCase.indexOf("established") > 0)
			return true;
		else
			return false;
	}

	/**
	 * ----------------------------------------------------------------------
	 */
	private String readHttpReply(InputStream input) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		InputStream in = input;
		boolean error = false;
		int newlinesSeen = 0;

		while (newlinesSeen < 2) {
			int i = in.read();
			if (i < 0) {
				throw new IOException("Unexpected EOF from proxy");
			}
			if (i == '\n') {
				++newlinesSeen;
			}
			else if (i != '\r') {
				newlinesSeen = 0;
			}
			baos.write(i);
		}

		byte[] msg = baos.toByteArray();
		String header = new String(msg);
		byte[] body = new byte[0];

		try {
			String field = extractField(header.toLowerCase(), "content-length:");
			int len = Integer.parseInt(field);
			System.out.println("message length " + len);
			body = new byte[len];
			input.read(body);
		}
		catch (IOException e) {}
		return header;
		// return new String(body, "UTF8");
	}

	/**
	 * ----------------------------------------------------------------------
	 */
	private String extractField(String header, String fieldName)
			throws IOException {
		int index = header.toLowerCase().indexOf(fieldName.toLowerCase());
		if (index < 0) {
			throw new IOException("No field '" + fieldName + "' in " + header);
		}
		int indexEnd = header.indexOf("\n", index);
		if (indexEnd < 0) indexEnd = header.length();
		String field = header.substring(index + fieldName.length(), indexEnd);
		return field.trim();
	}
}

class InboundThread extends Thread {

	InputStream in;
	String remoteHost;

	public InboundThread(InputStream in, String remoteHost) {
		this.in = in;
		this.remoteHost = remoteHost;
	}

	public void run() {
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		String line;
		int ch;
		try {
			while ((line = reader.readLine()) != null) {
				System.out.println(line);

			}
		}
		catch (Exception e) {}
	}

}

class PostRequest implements Serializable {
	private String initialLine;
	private String headers;
	private int contentLength;
	private String body;

	public PostRequest() {}

	void setInitialLine(String initialLine) {
		this.initialLine = initialLine;
	}

	String getInitialLine() {
		return initialLine;
	}

	void setHeaders(String headers) {
		this.headers = headers;
	}

	String getHeaders() {
		return headers;
	}

	void setBody(String body) {
		this.body = body;
	}

	String getBody() {
		return body;
	}

	public String toString() {
		return initialLine + headers + contentLength + body;
	}
}
